//
// Created by 17775 on 2024/10/9.
//

#include "LiveMode_Measurement.h"

LiveMode_Measurement::LiveMode_Measurement() = default;

int LiveMode_Measurement::_nDocIdLive = 0;
int LiveMode_Measurement::_iBeamLineDocId = 0;
int LiveMode_Measurement::_iBeamQualityDocId = 0;
int LiveMode_Measurement::_iBeamStabilityDocId = 0;
int LiveMode_Measurement::_iPointingStabilityDocId = 0;
int LiveMode_Measurement::_iSingleDocId = 0;
int LiveMode_Measurement::_iTimeSeriesDocId = 0;
int LiveMode_Measurement::_iDivergenceDocId = 0;
int LiveMode_Measurement::_iSnapshotDocId = 0;


void LiveMode_Measurement::setSingleFrameSpan(const int frames) {
    RayCi_Single_Recording_Settings_setFrameSpan(_iSingleDocId, frames);
}

void LiveMode_Measurement::setSingleTimeSpan(const double time) {
    RayCi_Single_Recording_Settings_setTimeSpan(_iSingleDocId, time);
}

void LiveMode_Measurement::newSingleId() {
    RayCi_LiveMode_Measurement_newSingle(_nDocIdLive, &_iSingleDocId);
}

QImage LiveMode_Measurement::startSingle() {
    bool bAvailable = false;
    RayCi_Single_Recording_isAvailable(_iSingleDocId, &bAvailable);
    if(bAvailable) {
//        RayCi_Single_Recording_Settings_setFrameSpan(_iSingleDocId, 8);
        RayCi_Single_Recording_start(_iSingleDocId);
        bool bRecording = true;
        while (bRecording) {
            RayCi_Single_Recording_isRecording(_iSingleDocId, &bRecording);
        }
        int width = 0, height = 0;
        RayCi_Single_Data_getSizeX(_iSingleDocId, &width);
        RayCi_Single_Data_getSizeY(_iSingleDocId, &height);
        nSizeX = width, nSizeY = height;
        auto nSize = width * height;
        vecPreview.resize(nSize);
        RayCi_Single_Data_getPreview(_iSingleDocId, &vecPreview[0], &nSize);
        RayCiLut::InitImage(singleImage, _iSingleDocId, width, height);
        for (int y = 0; y < height; ++y) {
            std::memcpy(singleImage.scanLine(y), &vecPreview[y * width], width * sizeof(quint8));
        }
    }
    return singleImage;
}

void LiveMode_Measurement::newTimeSeriesId() {
    RayCi_LiveMode_Measurement_newTimeSeries(_nDocIdLive, &_iTimeSeriesDocId);
}

void LiveMode_Measurement::startTimeSeries() {
    bool bAvailable = false;
    RayCi_TimeSeries_Recording_isAvailable(_iTimeSeriesDocId, &bAvailable);
    if(bAvailable) {
        RayCi_TimeSeries_Recording_start(_iTimeSeriesDocId);
    }
    m_timeSeriesTask = QtConcurrent::run([=]() {
        bool bRecording = true;
        preItemCount = 0;
        int32_t iItemCount = 0;
        QThread::msleep(300);
        while (bRecording) {
            RayCi_TimeSeries_Sequence_getItemCount(_iTimeSeriesDocId, &iItemCount);
            if(preItemCount != iItemCount) {
                preItemCount = iItemCount;
                emit newTimeSeriesSequence(iItemCount);
            }
            RayCi_TimeSeries_Recording_isRecording(_iTimeSeriesDocId, &bRecording);
            QThread::msleep(100);
        }
    });
}

QImage LiveMode_Measurement::getTimeSeriesData() {
    bool bValid = false;
    RayCi_TimeSeries_isValid(_iTimeSeriesDocId, &bValid);
    if (!bValid) {
        return {};
    }
    int width = 0, height = 0;
    RayCi_Single_Data_getSizeX(_iTimeSeriesDocId, &width);
    RayCi_Single_Data_getSizeY(_iTimeSeriesDocId, &height);
    nSizeX = width;
    nSizeY = height;
    auto nSize = width * height;
    vecTimeSeriesPreview.resize(width * height);
    RayCi_Single_Data_getPreview(_iTimeSeriesDocId, vecTimeSeriesPreview.data(), &nSize);
    RayCiLut::InitImage(timeSeriesImage, _iTimeSeriesDocId, width, height);
    for (int y = 0; y < height; ++y) {
        std::memcpy(timeSeriesImage.scanLine(y), vecTimeSeriesPreview.data() + y * width, width * sizeof(quint8));
    }
    return timeSeriesImage;
}

void LiveMode_Measurement::newDivergenceId() {
    RayCi_LiveMode_Measurement_newDivergence(_nDocIdLive, &_iDivergenceDocId);
}

void LiveMode_Measurement::startDivergence() {
    bool bAvailable = false;
    RayCi_Divergence_Recording_isAvailable(_iDivergenceDocId, &bAvailable);
    if(bAvailable) {
        RayCi_Divergence_Recording_start(_iDivergenceDocId);
    }
}

QImage LiveMode_Measurement::getDivergenceImageData() {
    bool bRecording = true;
    while(bRecording) {
        RayCi_Divergence_Recording_isRecording(_iDivergenceDocId, &bRecording);
    }
//    bool bValid = false;
//    RayCi_Divergence_isValid(_iDivergenceDocId, &bValid);
//    if (!bValid) {
//        return {};
//    }
    int width = 0, height = 0;
    RayCi_Single_Data_getSizeX(_iDivergenceDocId, &width);
    RayCi_Single_Data_getSizeY(_iDivergenceDocId, &height);
    nSizeX = width;
    nSizeY = height;
    auto nSize = width * height;
    vecDivergencePreview.resize(width * height);
    RayCi_Single_Data_getPreview(_iDivergenceDocId, vecDivergencePreview.data(), &nSize);
    RayCiLut::InitImage(divergenceImage, _iDivergenceDocId, width, height);
    for (int y = 0; y < height; ++y) {
        std::memcpy(divergenceImage.scanLine(y), vecDivergencePreview.data() + y * width, width * sizeof(quint8));
    }
    return divergenceImage;
}

void LiveMode_Measurement::newBeamLineId() {
    RayCi_LiveMode_Measurement_newBeamLine(_nDocIdLive, &_iBeamLineDocId);
}

void LiveMode_Measurement::startBeamLine() {
    bool bAvailable = false;
    RayCi_BeamLine_Recording_isAvailable(_iBeamLineDocId, &bAvailable);
    if(bAvailable) {
        RayCi_BeamLine_Recording_start(_iBeamLineDocId);
    }
    bool bRecording = true;
    while(bRecording) {
        RayCi_BeamLine_Recording_isRecording(_iBeamLineDocId, &bRecording);
    }
}

QImage LiveMode_Measurement::getBeamLineImageData() {
    bool bValid = false;
    RayCi_BeamLine_isValid(_iBeamLineDocId, &bValid);
    if (!bValid) {
        return {};
    }
    int width = 0, height = 0;
    RayCi_Single_Data_getSizeX(_iBeamLineDocId, &width);
    RayCi_Single_Data_getSizeY(_iBeamLineDocId, &height);
    nSizeX = width;
    nSizeY = height;
    auto nSize = width * height;
    vecBeamLinePreview.resize(width * height);
    RayCi_Single_Data_getPreview(_iBeamLineDocId, vecBeamLinePreview.data(), &nSize);
    RayCiLut::InitImage(beamLineImage, _iBeamLineDocId, width, height);
    for (int y = 0; y < height; ++y) {
        std::memcpy(beamLineImage.scanLine(y), vecBeamLinePreview.data() + y * width, width * sizeof(quint8));
    }
    return beamLineImage;
}

void LiveMode_Measurement::newBeamQualityId() {
    RayCi_LiveMode_Measurement_newBeamQuality(_nDocIdLive, &_iBeamQualityDocId);
}

void LiveMode_Measurement::startBeamQuality() {
    bool bAvailable = false;
    RayCi_BeamQuality_Recording_isAvailable(_iBeamQualityDocId, &bAvailable);
    if (!bAvailable) {
        return;
    }
    bool IsFrameSpan= false;
    RayCi_BeamQuality_Recording_Settings_isFrameSpan(_iBeamQualityDocId, &IsFrameSpan);
    while (isExposureControlling()) {
        QThread::msleep(50);
    }

    int frameSpan = 0;
    double estimateFrameTime=300; // 估计帧间隔时间300ms
    RayCi_BeamQuality_Recording_Settings_getFrameSpan(_iBeamQualityDocId, &frameSpan);
    qDebug() << "BeamQuality FrameSpan: " << frameSpan;

    double timeSpan=0;
    RayCi_BeamQuality_Recording_Settings_getTimeSpan(_iBeamQualityDocId, &timeSpan);
    qDebug() << "BeamQuality TimeSpan: " << timeSpan;

    // 开始录制
    QElapsedTimer recordingTimer;
    recordingTimer.start();
    RayCi_BeamQuality_Recording_start(_iBeamQualityDocId);

    bool bRecording = true;
    double estimatedFrame = 0;
    while (bRecording) {
        RayCi_BeamQuality_Recording_isRecording(_iBeamQualityDocId, &bRecording);
        const double elapsedTime = recordingTimer.elapsed();
        int progress = 0;
        if (estimatedFrame<frameSpan)
        estimatedFrame = std::min(static_cast<int>(elapsedTime / estimateFrameTime), frameSpan);
        else
            estimatedFrame = static_cast<int>(elapsedTime / estimateFrameTime);

        if (IsFrameSpan)
            progress = std::min((estimatedFrame / static_cast<double>(frameSpan)) * 100.0, 100.0);
        else {
            progress= std::min((elapsedTime / timeSpan) /10.0, 100.0);
        }

        emit BeamQualityProgress(elapsedTime, estimatedFrame, progress,IsFrameSpan);
        qDebug() << "BeamQuality Progress: " << elapsedTime << "ms, " << estimatedFrame << ", " << progress << "%";
        QThread::msleep(10);
    }
    frameSpan=estimatedFrame;
    emit BeamQualityProgress(recordingTimer.elapsed(), frameSpan, 100,IsFrameSpan);
}



QImage LiveMode_Measurement::getBeamQualityImageData() {
    bool bValid = false;
    RayCi_BeamQuality_isValid(_iBeamQualityDocId, &bValid);
    // if (!bValid) {
    //     return {};
    // }
    int width = 0, height = 0;
    RayCi_Single_Data_getSizeX(_iBeamQualityDocId, &width);
    RayCi_Single_Data_getSizeY(_iBeamQualityDocId, &height);
    nSizeX = width;
    nSizeY = height;
    auto nSize = width * height;
    vecBeamQualityPreview.resize(width * height);
    RayCi_Single_Data_getPreview(_iBeamQualityDocId, vecBeamQualityPreview.data(), &nSize);
    RayCiLut::InitImage(beamQualityImage, _iBeamQualityDocId, width, height);
    for (int y = 0; y < height; ++y) {
        std::memcpy(beamQualityImage.scanLine(y), vecBeamQualityPreview.data() + y * width, width * sizeof(quint8));
    }
    return beamQualityImage;
}

void LiveMode_Measurement::setBeamQualityPosition(double position) {
    RayCi_BeamQuality_Recording_Settings_setPosition(_iBeamQualityDocId, position);
}


QImage LiveMode_Measurement::newSnapshot() {
    RayCi_LiveMode_Measurement_newSnapshot(_nDocIdLive, &_iSnapshotDocId);
    int width = 0, height = 0;
    RayCi_Single_Data_getSizeX(_iSnapshotDocId, &width);
    RayCi_Single_Data_getSizeY(_iSnapshotDocId, &height);
    nSizeX = width, nSizeY = height;
    auto nSize = width * height;
    vecPreview.resize(nSize);
    RayCi_Single_Data_getPreview(_iSnapshotDocId, &vecPreview[0], &nSize);

    RayCiLut::InitImage(snapshotImage, _iSnapshotDocId, width, height);
    for (int y = 0; y < height; ++y) {
        std::memcpy(snapshotImage.scanLine(y), &vecPreview[y * width], width * sizeof(quint8));
    }
    return snapshotImage;
}

void LiveMode_Measurement::closeSingleReportWindow() {
    RayCi_Single_Report_Window_close(_iSnapshotDocId);
}

void LiveMode_Measurement::closeSingle(const bool bWaitWhileClosing) {
    RayCi_Single_close(_iSnapshotDocId, bWaitWhileClosing);
}

void LiveMode_Measurement::closeSnapshot(bool bWaitWhileClosing) {
    RayCi_Single_close(_iSingleDocId, bWaitWhileClosing);
}

std::vector<float> LiveMode_Measurement::newSnapshotFloatData() {
    int nSize = nSizeX * nSizeY;
    vecSnapshotData.resize(nSize);
    RayCi_Single_Data_getFloatField(_iSnapshotDocId, vecSnapshotData.data(), &nSize);
    return vecSnapshotData;
}

SnapShotAOI LiveMode_Measurement::getPositionSize(int id) const {
    SnapShotAOI aoi;
    RayCi_Single_AOI_getPositionX_px(id, &aoi.dPositionX_px);
    RayCi_Single_AOI_getPositionY_px(id, &aoi.dPositionY_px);
    RayCi_Single_AOI_getMajor_px(id, &aoi.dMajor_px);
    RayCi_Single_AOI_getMinor_px(id, &aoi.dMinor_px);
    RayCi_Single_AOI_getAzimuthAngle_px(id, &aoi.dAzimuthAngle_px);
    RayCi_Single_AOI_getShape(id, &aoi.nShape);
    return aoi;
}
void LiveMode_Measurement::setAOIShape(int id, int shape) const {
    RayCi_Single_AOI_setShape(id, shape);
}
void LiveMode_Measurement::setPosition_px(int id, double dPosX, double dPosY) const {
    RayCi_Single_AOI_setPosition_px(id, dPosX, dPosY);
}
double LiveMode_Measurement::getAzimuthAngle(int id) const {
    double _dPhi = 0;
    RayCi_Single_AOI_getAzimuthAngle_px(id, &_dPhi);
    return _dPhi;
}
void LiveMode_Measurement::setAOISize(int id, double dMajor, double dMinor, double dPhi) const {
    RayCi_Single_AOI_setSize_px(id, dMajor, dMinor, dPhi);
}
void LiveMode_Measurement::setHorizontalReference(int id, bool isXImageOrAOI, int index) const {
    isXImageOrAOI ? RayCi_Single_Settings_CoordSystem_Horizontal_setReference(id, index) : RayCi_Single_Settings_CoordSystem_Vertical_setReference(id, index);
}
void LiveMode_Measurement::setHorizontalOrigin(int id, bool isLeftOrTop, int index) const {
    isLeftOrTop ? RayCi_Single_Settings_CoordSystem_Horizontal_setOrigin(id, index - 1) : RayCi_Single_Settings_CoordSystem_Vertical_setOrigin(id, index -1);
}
double LiveMode_Measurement::getUserDefinedPositionX(int id) const {
    double dPositionX = 0;
    RayCi_Single_Settings_CoordSystem_UserDefined_getPositionX(id, &dPositionX);
    return dPositionX;
}
double LiveMode_Measurement::getUserDefinedPositionY(int id) const {
    double dPositionY = 0;
    RayCi_Single_Settings_CoordSystem_UserDefined_getPositionY(id, &dPositionY);
    return dPositionY;
}
void LiveMode_Measurement::setCoordSystemMode(int id, int iMode) const {
    switch (iMode) {
        case 0: {
            RayCi_Single_Settings_CoordSystem_setDefault(id);
            break;
        }
        case 1: {
            RayCi_Single_Settings_CoordSystem_setAutomatic(id);
            break;
        }
        case 2: {
            RayCi_Single_Settings_CoordSystem_setUserDefined(id);
            break;
        }
    }
}
void LiveMode_Measurement::setUserDefined(int id, double dxPos, double dyPos) const {
    RayCi_Single_Settings_CoordSystem_UserDefined_setPosition(id, dxPos, dyPos);
}

void LiveMode_Measurement::reportSnapShot() {
    RayCi_createDirectoryA("C:\\RayCi\\SnapShot");
    OPENFILENAMEA ofn;
    char szFile[260];
    ZeroMemory(&ofn, sizeof(ofn));
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = nullptr;
    ofn.lpstrFile = szFile;
    ofn.nMaxFile = sizeof(szFile) / sizeof(szFile[0]) - 1;
    ofn.lpstrFilter = "PDF Files(*.pdf)\0*.pdf*\0All Files(*.*)\0*.*\0\0";
    ofn.nFilterIndex = 1;
    ofn.lpstrInitialDir = "C:\\RayCi\\SnapShot";
    ofn.lpstrTitle = "Save Report As";
    ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
    if (GetSaveFileNameA(&ofn) == TRUE) {
        const char* reportPathName = ofn.lpstrFile;
        std::string reportPathWithExtension = std::string(reportPathName) + ".pdf";
        RayCi_Single_Report_exportA(_iSnapshotDocId, reportPathWithExtension.c_str());
        ShellExecuteA(nullptr, "open", reportPathWithExtension.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
    } else {
        MessageBoxA(nullptr, "Export was cancelled.", "Info", MB_OK | MB_ICONINFORMATION);
    }
}

std::vector<float> LiveMode_Measurement::newSingleFloatData() {
    int nSize = nSizeX * nSizeY;
    vecSingleData.resize(nSize);
    RayCi_Single_Data_getFloatField(_iSingleDocId, vecSingleData.data(), &nSize);
    return vecSingleData;
}

std::vector<float> LiveMode_Measurement::newTimeSeriesFloatData() {
    int nSize = nSizeX * nSizeY;
    vecTimeSeriesData.resize(nSize);
    RayCi_Single_Data_getFloatField(_iTimeSeriesDocId, vecTimeSeriesData.data(), &nSize);
    return vecTimeSeriesData;
}

std::vector<float> LiveMode_Measurement::newDivergenceFloatData() {
    int nSize = nSizeX * nSizeY;
    vecDivergenceData.resize(nSize);
    RayCi_Single_Data_getFloatField(_iDivergenceDocId, vecDivergenceData.data(), &nSize);
    return vecDivergenceData;
}

std::vector<float> LiveMode_Measurement::newBeamLineFloatData() {
    int nSize = nSizeX * nSizeY;
    vecBeamLineData.resize(nSize);
    RayCi_Single_Data_getFloatField(_iBeamLineDocId, vecBeamLineData.data(), &nSize);
    return vecBeamLineData;
}


std::vector<float> LiveMode_Measurement::newBeamQualityFloatData() {
    int nSize = nSizeX * nSizeY;
    vecBeamQualityData.resize(nSize);
    RayCi_Single_Data_getFloatField(_iBeamQualityDocId, vecBeamQualityData.data(), &nSize);
    return vecBeamQualityData;
}


bool LiveMode_Measurement::isExposureControlling() {
    bool bControlling = true;
    RayCi_LiveMode_isExposureControlling(_nDocIdLive, &bControlling);
    return bControlling;
}
bool LiveMode_Measurement::isAutoContrast_Single() {
    bool bAutoContrast = false;
    RayCi_Single_Settings_Lut_isAutoContrast(_iSnapshotDocId, &bAutoContrast);
    return bAutoContrast;
}

void LiveMode_Measurement::newBeamStabilityId() {
    RayCi_LiveMode_Measurement_newBeamStability(_nDocIdLive, &_iBeamStabilityDocId);
}

void LiveMode_Measurement::startBeamStability() {
    bool bAvailable = false;
    RayCi_BeamStability_Recording_isAvailable(_iBeamStabilityDocId, &bAvailable);
    if(bAvailable) {
        RayCi_BeamStability_Recording_start(_iBeamStabilityDocId);
        char szRemark_buffer[80];
        int nSize = sizeof(szRemark_buffer);
        RayCi_BeamStability_Recording_getRemarkA(_iBeamStabilityDocId, szRemark_buffer, &nSize);
    }
}

void LiveMode_Measurement::newPointingStabilityId() {
    RayCi_LiveMode_Measurement_newPointingStability(_nDocIdLive, &_iPointingStabilityDocId);
}

void LiveMode_Measurement::startPointingStability() {
    bool bAvailable = false;
    RayCi_PointingStability_Recording_isAvailable(_iPointingStabilityDocId, &bAvailable);
    if(bAvailable) {
        RayCi_PointingStability_Recording_start(_iPointingStabilityDocId);
        char szRemark_buffer[80];
        int nSize = sizeof(szRemark_buffer);
        RayCi_PointingStability_Recording_getRemarkA(_iPointingStabilityDocId, szRemark_buffer, &nSize);
    }
}


void LiveMode_Measurement::getAutoLut(const int32_t nIdDoc) {
    RayCiLut::GetActualLut(nIdDoc, RayCiLut::LUT, 8);
}

void LiveMode_Measurement::getBaseLut(const int32_t nIdDoc) {
  RayCiLut::GetBaseLut(nIdDoc);
}


void LiveMode_Measurement::setLutThreshold(const double dMin, const double dMax) {
    RayCi_Single_Settings_Lut_setThreshold(_nDocIdLive, dMin, dMax);
}

void LiveMode_Measurement::setAutoContrast(QImage &image, const int32_t nIdDoc, const bool bAutoContrast) {
    RayCi_Single_Settings_Lut_setAutoContrast(nIdDoc, bAutoContrast);
    RayCiLut::GetActualLut(nIdDoc, RayCiLut::LUT, 8);
    RayCiLut::SetNewColorTable(image);
}

void LiveMode_Measurement::setNewColorTable(QImage &image) {

    RayCiLut::SetNewColorTable(image);
}